// Copyright (C) 2012 Yury Bychenkov (http://bychenkov.com).
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//  - Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//  - Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//  - Neither the name of Yury Bychenkov nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include "numberutil.h"

namespace NumberUtil {

bool parseInteger(const char* s, int64& value, const char** end) {
	if (end != 0) {
		*end = s;
	}

	bool negative = *s == '-';
	if (*s == '+' || *s == '-') {
		s++;
	}

	if ('0' <= *s && *s <= '9') {
		int64 v = 0;

		do {
			// TODO check overflow
			v = v * 10 + (*s - '0');
			s++;
		} while ('0' <= *s && *s <= '9');

		value = negative? -v : v;
	
		if (end != 0) {
			*end = s;
		}

		return true;
	}

	return false;
}

int parseDecimal(const char* s, uint8* data, uint dataSize, const char** end, const char decimalSeparator) {

	if (end) {
		*end = s;
	}
	
	int sign = 1;

	if (*s == '+' || *s == '-') {
		sign = *s == '-'? -1: 1;
		s++;
	}

	const char* firstDigit = 0;
	const char* lastDigit = 0;
	const char* point = 0;

	while (true) {
		char ch = *s;

		if ('0' <= ch && ch <= '9') {
			if (firstDigit == 0 || *firstDigit == '0') {
				firstDigit = s;
			}
			if (ch != '0') {
				lastDigit = s;
			}
		} else if (ch == decimalSeparator) {
			if (point != 0) {
				break;
			}
			point = s;
		} else {
			if (point == 0) {
				point = s;
			}
			break;
		}

		s++;
	}

	if (firstDigit == 0) {
		return 0;
	}

	int scale = 0;

	if (*s == 'e' || *s == 'E') {
		int esign = 1;
		s++;
		if (*s == '+' || *s == '-') {
			esign = *s == '-'? -1: 1;
			s++;
		}
		for ( ; '0' <= *s && *s <= '9'; s++) {
			scale = scale * 10 + (*s - '0');
		}
		scale *= esign;
	}

	scale += point - firstDigit + (point < firstDigit? 1: 0);
	if (scale < -63 || scale > 63) {
		return 0;
	}

	if (end) {
		*end = s;
	}

	if (lastDigit == 0) {
		if (dataSize < 2) {
			return -2;
		}
		data[0] = 0xC0;
		data[1] = 0;
		return 2;
	}



	const uint digitCount = lastDigit - firstDigit + (firstDigit <= point && point <= lastDigit? 0: 1);
	const uint size = (digitCount + 1) / 2 + 1;

	if (size > dataSize) {
		return -size;
	}

	*data++ = (sign < 0? 0x00: 0x80) | ((scale + 63) & 0x7F);

	uint8 value = 1;
	for (s = firstDigit; s <= lastDigit; s++) {
		char ch = *s;

		if (ch != decimalSeparator) {
			value = value * 10 + (ch - '0');
		}

		if (s == lastDigit && value < 100) {
			value *= 10;
		}

		if (value >= 100) {
			*data++ = ((value - 100) << 1) | (s == lastDigit? 0: 1);
			value = 1;
		}
	}

	return size;
}

int sizeOfDecimal(const uint8* data) {
	if (*data == 0xFF) {
		return 1;
	}
	const uint8* p = data + 1;
	while (*p & 1) {
		p++;
	}
	return p - data + 1;
}

int formatDecimal(ostream& os, const uint8* data, const char decimalSeparator) {
	if (*data == 0xFF) {
		os << "null";
		return 1;
	}

	stringstream ss;
	
	const uint8* p = data;

	uint8 d = *p++;
	ss << ((d & 0x80) == 0? "-0": "0") << decimalSeparator;

	while (true) {
		uint8 v = *p++;
		uint8 w = v >> 1;
		if (w > 99) {
			ss << "??";
		} else {
			ss << (w < 10? "0": "") << int(w);
		}
		if ((v & 1) == 0) {
			break;
		}
	}

	int scale = int(d & 0x7F) - 63;
	if (scale != 0) {
		ss << "E" << scale;
	}

	os << ss.str();
	
	return p - data;
}

string formatHex(uint64 v, uint padding) {
	char result[17] = "0000000000000000";
	int p = padding <= 0? 1: (padding > 16? 16: padding);

	int i;
	for (i = 15; i >= 0 && v > 0; i--, v >>= 4, p--) {
		char ch = (char)(v & 0xF);
		result[i] = ch < 10? ch + '0': ch + 'a' - 10;
	}
	if (p > 0) {
		i -= p;
	}

	return string(result + i + 1);
}

} // namespace NumberUtil
